
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;

using Swaf;
using Swaf.BizObj;

namespace Swaf.Gui.AspNet2
{
    /// <summary>
    /// Manages the process of trying to localize an ASP.NET control based on the type of control
    /// it is and the properties which support localization.  This class has a hard coded list of controls
    /// and properties it will localize.  The actual process of localizing a value is done by the 
    /// localization manager of CSF.
    /// </summary>
    public class ServerControlLocalizer
    {
        /// <summary>
        /// This dictionary is used to keep track of all control/property values that are actually
        /// attempted to be localized, wether or not the localization was successful (a new value
        /// returned from the localization manager).
        /// </summary>
        protected static Dictionary<string, List<LocalizeAttemptInfo>> s_localizedValues;
		protected static Dictionary<string, IBizObjList> s_pages;
		protected static IKeyedBizObjList s_localizationAttempts;

		protected static bool s_setupTracking = false;
        protected static bool s_trackLocalizedProperties = false;
        protected static bool s_trackDefaultCultureOnly = true;
        protected static bool s_trackOnlyNonLocalizedProperties = true;
        protected static string s_defaultCultureName = "en-us";
        protected static string s_pageCalcNameProperty = null;
        protected static bool s_showCultureOnNoLocalizationHit = false;
        protected static Dictionary<Type, LocalizationInfo> s_controlFieldLocations;
        protected static LocalizationInfo s_empty = new LocalizationInfo(null);

		protected string m_pageName = null;

        public static bool TrackLocalizedProperties 
        { 
            get 
			{
				if (!s_setupTracking && bool.TryParse(Application.globalApp.resMgr.getMacro("TrackControlLocalization"), out s_trackLocalizedProperties))
					TrackLocalizedProperties = true;
				return s_trackLocalizedProperties; 
			} 
            set 
            {
				s_setupTracking = true;
				if (value && s_localizedValues == null)
				{
					s_localizedValues = new Dictionary<string, List<LocalizeAttemptInfo>>();
					s_localizationAttempts = Application.globalApp.bizObjMgr.createList("UiView").getIndex("name");
				}
                if (!value)
				{
                    s_localizedValues = null;
					s_localizationAttempts = null;
				}

                s_trackLocalizedProperties = value; 
            } 
        }

        public static string PageCalcNameProperty
        {
            get { return s_pageCalcNameProperty; }
            set { s_pageCalcNameProperty = value; }
        }

        public class PropertyLocalizationInfo
        {
            public PropertyInfo Property;
            public bool HumanReadable;
            public PropertyLocalizationInfo(PropertyInfo prop, bool humanReadable)
            {
                Property = prop;
                HumanReadable = humanReadable;
            }
        }

        public class LocalizeAttemptInfo
        {
            public string Key;
            public string ControlId;
            public string ControlPropertyName;
            public string DefaultValue;
            public string CultureName;
            public bool LocalizeSuccessful;
            public LocalizeAttemptInfo(string key, string controlId, string propName, string defVal, string cultureName, bool localizeSuccessful)
            {
                Key = key;
                ControlId = controlId;
                ControlPropertyName = propName;
                DefaultValue = defVal;
                CultureName = cultureName;
                LocalizeSuccessful = localizeSuccessful;
            }
        }

        public class LocalizationInfo
        {
            public Type CtlType = null;
            public List<PropertyLocalizationInfo> Properties;
            public PropertyInfo ChildItemsProperty = null;
            public PropertyInfo ChildItemLocalName = null;
            public PropertyInfo ChildItemKey = null;
            public PropertyInfo ChildItemRecurse = null;
            public bool RequiresUnderscoreIdToLocalize = true;
            public LocalizationInfo(Type ctlType, PropertyInfo childItemsProp, PropertyInfo childItemsLocalName, PropertyInfo childKey, PropertyInfo recurse)
                :this(ctlType)
            {
                ChildItemsProperty = childItemsProp;
                ChildItemLocalName = childItemsLocalName;
                ChildItemKey = childKey;
                ChildItemRecurse = recurse;
            }

            public LocalizationInfo(Type ctlType)
            {
                CtlType = ctlType;
                Properties = new List<PropertyLocalizationInfo>();
            }
            public LocalizationInfo(Type ctlType, bool requiresUnderscoreId)
                : this(ctlType)
            {
                RequiresUnderscoreIdToLocalize = requiresUnderscoreId;
            }

            public void addProperty(PropertyInfo prop, bool humanReadable)
            {
                Properties.Add(new PropertyLocalizationInfo(prop, humanReadable));
            }

            public bool IsLocalizable(Control ctl)
            {
                return CtlType.IsAssignableFrom(ctl.GetType()) && (!RequiresUnderscoreIdToLocalize || (ctl.ID.StartsWith("_")));
            }
        }

		public ServerControlLocalizer()
		{
		}
		public ServerControlLocalizer(string pageName)
		{
			m_pageName = pageName;
		}

		protected static Dictionary<Type, LocalizationInfo> ControlMap
		{
			get
			{
				if(s_controlFieldLocations == null)
					s_controlFieldLocations = new Dictionary<Type, LocalizationInfo>();
				return s_controlFieldLocations;
			}
		}

        static ServerControlLocalizer()
        {
            Type ctlType = null;
			s_pages = new Dictionary<string, IBizObjList>();

            ctlType = typeof(System.Web.UI.WebControls.Label);
            ControlMap[ctlType] = new LocalizationInfo(ctlType);
			ControlMap[ctlType].addProperty(ctlType.GetProperty("Text"), true);

            ctlType = typeof(System.Web.UI.WebControls.HyperLink);
            ControlMap[ctlType] = new LocalizationInfo(ctlType);
			ControlMap[ctlType].addProperty(ctlType.GetProperty("Text"), true);

            ctlType = typeof(System.Web.UI.WebControls.IButtonControl);
            ControlMap[ctlType] = new LocalizationInfo(ctlType);
            ControlMap[ctlType].addProperty(ctlType.GetProperty("Text"), true);

            ctlType = typeof(System.Web.UI.WebControls.CheckBox);
            ControlMap[ctlType] = new LocalizationInfo(ctlType,false);
            ControlMap[ctlType].addProperty(ctlType.GetProperty("Text"), true);

            Type childType = typeof(System.Web.UI.WebControls.MenuItem);
            ctlType = typeof(System.Web.UI.WebControls.Menu);
			ControlMap[ctlType] = new LocalizationInfo(ctlType,
                ctlType.GetProperty("Items"), childType.GetProperty("Text"), childType.GetProperty("Value"), childType.GetProperty("ChildItems"));
        }

        public static Dictionary<string, List<LocalizeAttemptInfo>> LocalizationAttempts
        {
            get
            {
                return s_localizedValues;
            }
        }

		public IKeyedBizObjList UiViewLocalizationInfo { get { return s_localizationAttempts; } }

        public void localizeControl(Control control, Page page, string parentName)
        {
            localizeControl(control, calculateLocalizedProperties(control), page, parentName);
        }

        public void localizeControl(Control control, LocalizationInfo info, Page page, string parentName)
        {
            if (info.Properties.Count != 0 || info.ChildItemsProperty != null)
            {
                IApplication app = Application.currentApp;

                string pageName = calcPageName(page);

                foreach (PropertyLocalizationInfo prop in info.Properties)
                {
                    string key = string.Format("{0}|{1}|{2}",
                        parentName, control.ID, prop.Property.Name);

					IBizObjList pageCtls = null;
					if (!s_pages.TryGetValue(pageName, out pageCtls))
						s_pages[pageName] = pageCtls = app.ruleMgr.execute("getUiViewControlFields", pageName).CallResults as IBizObjList;
					
					string val = null;
					if(pageCtls != null)
					{
						IBizObj fld = pageCtls.search("fieldName", key);
						if(fld != null)
							val = app.resMgr.getMessage(fld.get("native.textId"), null);
					}

					if (TrackLocalizedProperties)
						trackLocalizationAttempt(app, page, pageName,
							string.Format("{0}|{1}", parentName, control.ID),
							key, prop.Property.GetValue(control, null) as string, val != null);

                    if (val == null && s_showCultureOnNoLocalizationHit && prop.HumanReadable)
                            val = hookValueWithCulture(app, prop.Property.GetValue(control, null) as string);
                    if (val != null)
                        prop.Property.SetValue(control, val, null);
                }

                if (info.ChildItemsProperty != null)
                {
                    IEnumerable children = info.ChildItemsProperty.GetValue(control, null) as IEnumerable;
                    processChildItems(app, control, info, page, pageName, parentName, children);
                }
            }
        }

        public string calcPageName(Page page)
        {
			if (m_pageName != null)
				return m_pageName;

            string pageName = null;
            if (s_pageCalcNameProperty == null)
            {
                pageName = page.GetType().Name;
                pageName = pageName.Substring(0, pageName.Length - 5); //remove trailing _aspx
            }
            else
                pageName = Cmn.callPropertyGet(page, s_pageCalcNameProperty) as string;
            return pageName;
        }

        protected string hookValueWithCulture(IApplication app, string strVal)
        {
            return hookValueWithCulture(app, strVal, true);
        }

        protected string hookValueWithCulture(IApplication app, string strVal, bool reallyHookValue)
        {
            string val = null;
            if (strVal != null && strVal != "")
            {
                Regex r = new Regex(@"^\[\w{2}-\w{2}\](.*)");
                Match m = r.Match(strVal);
                if (m.Success)
                    strVal = m.Groups[1].Value;

                val = reallyHookValue ? string.Format("[{0}] {1}", app.resMgr.CurrentCulture.get("browserCode"), strVal):strVal;
            }
            return val;
        }

        protected void processChildItems(IApplication app, Control control, LocalizationInfo info, Page page, string pageName, string parentName, IEnumerable children)
        {
            if (children != null)
                foreach (object child in children)
                {
					string key = string.Format("{0}|{1}|{2}",
						parentName, control.ID, info.ChildItemKey.GetValue(child, null));

					IBizObjList pageCtls = null;
					if (!s_pages.TryGetValue(pageName, out pageCtls))
						s_pages[pageName] = pageCtls = app.ruleMgr.execute("getUiViewControlFields", pageName).CallResults as IBizObjList;

					string val = null;
					if (pageCtls != null)
					{
						IBizObj fld = pageCtls.search("fieldName", key);
						if (fld != null)
							val = app.resMgr.getMessage(fld.get("native.textId"), null);
					}

                    if (TrackLocalizedProperties)
                        trackLocalizationAttempt(app, page, pageName, 
                            string.Format("{0}|{1}", parentName, control.ID),
                            key, info.ChildItemLocalName.GetValue(child, null) as string, val != null);

                    if (val == null && s_showCultureOnNoLocalizationHit)
                        val = hookValueWithCulture(app, info.ChildItemLocalName.GetValue(child, null) as string);
                    if (val != null)
                        info.ChildItemLocalName.SetValue(child, val, null);
                    if (info.ChildItemRecurse != null)
                        processChildItems(app, control, info, page, pageName, parentName, (IEnumerable)info.ChildItemRecurse.GetValue(child, null));
                }
        }

		public bool ShouldTrackPageFields
		{
			get
			{
				return ((!s_trackDefaultCultureOnly || (s_trackDefaultCultureOnly && Application.currentApp.resMgr.UsingBuiltInCulture))
					&& Cmn.IsTrue(Application.globalApp.resMgr.getMacro("TrackControlLocalization")));
			}
		}

        protected void trackLocalizationAttempt(IApplication app, Page page, string pageName, string controlId, string key, string defValue, bool successful)
        {
			if (((!s_trackOnlyNonLocalizedProperties || (s_trackOnlyNonLocalizedProperties && !successful)) && ShouldTrackPageFields))
            {
				lock (s_empty)
				{
					string propName = "";
					int index = key.LastIndexOf('|');
					if (index != -1)
						propName = key.Substring(index + 1);


					LocalizeAttemptInfo attempt = new LocalizeAttemptInfo(key, controlId, propName, hookValueWithCulture(app, defValue, false), app.resMgr.CurrentCulture.get("browserCode").ToString(), successful);
					List<LocalizeAttemptInfo> pageList = null;
					if (!s_localizedValues.ContainsKey(pageName))
					{
						pageList = new List<LocalizeAttemptInfo>();
						s_localizedValues[pageName] = pageList;
					}
					else
						pageList = s_localizedValues[pageName];
					pageList.Add(attempt);

					IBizObj uiView = s_localizationAttempts.find(pageName, false);
					if (uiView == null)
					{
						uiView = Application.globalApp.bizObjMgr.create("UiView");
						uiView.put("name", pageName);
						uiView.put("title", page.Title);
						s_localizationAttempts.Add(uiView);
					}
					if (uiView != null)
					{
						string fullCtrlFieldName = controlId + "|" + propName;
						IKeyedBizObjList fields = ((IBizObjList)uiView.get("fields")).getIndex("fieldName");
						IBizObj field = fields.find(fullCtrlFieldName, false);
						if (field == null)
						{
							field = Application.globalApp.bizObjMgr.create("UiControlField");
							field.put("viewName", pageName);
							field.put("fieldName", fullCtrlFieldName);
							field.put("native.text", defValue);
							fields.Add(field);
						}
					}
				}
            }
        }

        public LocalizationInfo calculateLocalizedProperties(Control control)
        {
            IApplication app = Application.currentApp;
            LocalizationInfo info = null;

            if (app != null && !Cmn.IsEmpty(control.ID) && (!app.resMgr.UsingBuiltInCulture || TrackLocalizedProperties))
            {
                Type myType = control.GetType();
                foreach (KeyValuePair<Type, LocalizationInfo> ctrl in s_controlFieldLocations)
                    //if (ctrl.Key.IsAssignableFrom(myType))
                    if(ctrl.Value.IsLocalizable(control))
                    {
                        info = ctrl.Value;
                        break;
                    }
            }
            return (info == null) ? s_empty : info;
        }
    }
}
